/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2011-12-05     Bernard      the first version
 * 2021-06-25     THEWON       add nand flash general purpose interface
 */

/*
 * COPYRIGHT (C) 2012, Shanghai Real Thread
 */

#include <drivers/mtd_nand.h>

#ifdef RT_USING_MTD_NAND

#include "rtdef.h"
#include "board.h"

#define MTD_NAND_DRV(x)    ((rt_nand_driver_t)(x))
#ifndef min
#define min(a,b) (a>b? b:a)
#endif

static rt_uint8_t *_fill_oob(rt_nand_driver_t chip, const rt_uint8_t *oob, rt_size_t len)
{
    rt_uint32_t boffs;
    rt_size_t bytes;
    const struct rt_mtd_oob_region *free = chip->free_layout;

    rt_memset(chip->buffers.oob_poi, 0xFF, chip->parent.oob_size);

    bytes = min(len, free->length);
    boffs = free->offset;

    rt_memcpy(chip->buffers.oob_poi + boffs, oob, bytes);

    return chip->buffers.oob_poi;
}

static rt_uint8_t *_transfer_oob(rt_nand_driver_t chip, rt_uint8_t *oob, rt_size_t len)
{
    rt_uint32_t boffs = 0;
    rt_size_t bytes = 0;
    const struct rt_mtd_oob_region *free = chip->free_layout;

    for (; free->length && len; free++, len -= bytes)
    {
        /* Read request not from offset 0? */
        bytes = min(len, free->length);
        boffs = free->offset;

        rt_memcpy(oob, chip->buffers.oob_poi + boffs, bytes);
        oob += bytes;
    }

    return chip->buffers.oob_poi;
}

static rt_err_t _read_page_hwecc(rt_nand_driver_t chip, rt_uint8_t *buf)
{
    rt_uint16_t i;
    rt_uint16_t eccbytes, stepsize, eccsteps, eccpos;
    rt_uint8_t *p = RT_NULL;
    rt_uint8_t *ecc_calc = RT_NULL;
    rt_uint8_t *ecc_code = RT_NULL;
    rt_err_t ret = RT_MTD_EOK;

    eccbytes = chip->ecc.ecc_bytes;
    stepsize = chip->ecc.ecc_stepsize;
    eccsteps = chip->ecc.ecc_step;
    eccpos = chip->ecc.layout->offset;

    ecc_calc = chip->buffers.ecc_calc;
    ecc_code = chip->buffers.ecc_code;

    p = buf;

    for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize)
    {
        chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_EN, 0, 0);
        chip->ops->read_buf(chip, p, stepsize);
        chip->ecc.calculate(chip, p, &ecc_calc[i]);
        chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_DIS, 0, 0);
    }

    chip->ops->read_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size);
    rt_memcpy(ecc_code, &chip->buffers.oob_poi[eccpos], chip->ecc.layout->length);

    eccsteps = chip->ecc.ecc_step;
    p = buf;

    for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize)
    {
        int stat;

        stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
        if (stat == 1)
        {
            ret = -RT_MTD_EECC_CORRECT;
        }
        else if (stat == -1)
        {
            ret = -RT_MTD_EECC;
        }
    }

    return ret;
}

static rt_err_t _write_page_hwecc(rt_nand_driver_t chip, const rt_uint8_t *buf)
{
    rt_uint16_t i;
    rt_uint16_t eccbytes, stepsize, eccsteps, eccpos;
    rt_uint8_t *ecc_calc = RT_NULL;
    const rt_uint8_t *p = buf;

    eccbytes = chip->ecc.ecc_bytes;
    stepsize = chip->ecc.ecc_stepsize;
    eccsteps = chip->ecc.ecc_step;
    eccpos = chip->ecc.layout->offset;

    ecc_calc = chip->buffers.ecc_calc;

    for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize)
    {
        chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_EN, 0, 0);
        chip->ops->write_buf(chip, p, stepsize);
        chip->ecc.calculate(chip, p, &ecc_calc[i]);
        chip->ops->cmdfunc(chip, RT_NAND_CMD_ECC_DIS, 0, 0);
    }

    rt_memcpy(&chip->buffers.oob_poi[eccpos], ecc_calc, chip->ecc.layout->length);

    chip->ops->write_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size);

    return RT_MTD_EOK;
}

static rt_err_t _read_oob_std(rt_nand_driver_t chip, rt_off_t page)
{
    chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_RD, page, chip->parent.page_size);
    chip->ops->read_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size);

    return RT_MTD_EOK;
}

static rt_err_t _write_oob_std(rt_nand_driver_t chip, rt_off_t page)
{
    int status;

    chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_BGN, page, chip->parent.page_size);
    chip->ops->write_buf(chip, chip->buffers.oob_poi, chip->parent.oob_size);
    /* Send command to program the OOB data */
    status = chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_END, -1, -1);

    return status & NAND_ERROR ? -RT_MTD_EIO : RT_MTD_EOK;
}

static rt_err_t _only_read_oob(rt_nand_driver_t chip,
                                rt_off_t page,
                                rt_uint8_t *spare,
                                rt_size_t spare_len)
{
    int len;
    rt_uint8_t *oobbuf = spare;
    rt_size_t ooblen = spare_len;
    rt_err_t ret = RT_MTD_EOK;

    len = chip->free_layout->length;

    _read_oob_std(chip, page);

    len = min(len, ooblen);
    oobbuf = _transfer_oob(chip, oobbuf, len);

    return ret;
}

static rt_err_t _only_write_oob(rt_nand_driver_t chip,
                                rt_off_t page,
                                const rt_uint8_t *spare,
                                rt_size_t spare_len)
{
    const rt_uint8_t *oobbuf = spare;
    rt_size_t ooblen = spare_len;
    rt_err_t ret = RT_MTD_EOK;

    _fill_oob(chip, oobbuf, ooblen);

    ret = _write_oob_std(chip, page);

    return ret;
}

static rt_err_t _do_read_desc(rt_nand_driver_t chip,
                          rt_off_t page,
                          rt_uint8_t *data, rt_uint32_t data_len,
                          rt_uint8_t *spare, rt_uint32_t spare_len)
{
    rt_uint8_t *oobbuf = spare;
    rt_size_t ooblen = spare_len;
    rt_err_t ret = RT_MTD_EOK;

    chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_RD, page, 0x00);
    ret = _read_page_hwecc(chip, data);
    if (ret != RT_MTD_EOK)
    {
        return ret;
    }

    if (oobbuf != RT_NULL)
    {
        ooblen = min(spare_len, chip->free_layout->length);
        if (ooblen)
        {
            oobbuf = _transfer_oob(chip, oobbuf, ooblen);
        }
    }

    return ret;
}

static rt_err_t _do_write_desc(rt_nand_driver_t chip,
                          rt_off_t page,
                          const rt_uint8_t *data, rt_uint32_t data_len,
                          const rt_uint8_t *spare, rt_uint32_t spare_len)
{
    const rt_uint8_t *oobbuf = spare;
    rt_size_t ooblen = spare_len;
    rt_err_t ret = RT_MTD_EOK;

    if (oobbuf != RT_NULL)
    {
        ooblen = min(spare_len, chip->free_layout->length);
        if (ooblen)
        {
            oobbuf = _fill_oob(chip, oobbuf, ooblen);
        }
    }
    else
    {
        /* We still need to erase leftover OOB data */
        rt_memset(chip->buffers.oob_poi, 0xFF, chip->parent.oob_size);
    }
    chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_BGN, page, 0x00);

    _write_page_hwecc(chip, data);

    ret = chip->ops->cmdfunc(chip, RT_NAND_CMD_PAGE_WR_END, -1, -1);

    return ret;
}

static rt_err_t _read_page(rt_mtd_nand_t device,
                          rt_off_t page,
                          rt_uint8_t *data, rt_uint32_t data_len,
                          rt_uint8_t *spare, rt_uint32_t spare_len)
{
    rt_err_t ret = RT_MTD_EOK;
    rt_nand_driver_t chip = MTD_NAND_DRV(device);

    if (data == RT_NULL || data_len == 0)
    {
        ret = _only_read_oob(chip, page, spare, spare_len);
    }
    else
    {
        ret = _do_read_desc(chip, page, data, data_len, spare, spare_len);
    }

    return ret;
}

static rt_err_t _write_page(rt_mtd_nand_t device,
                           rt_off_t page,
                           const rt_uint8_t *data, rt_uint32_t data_len,
                           const rt_uint8_t *spare, rt_uint32_t spare_len)
{
    rt_err_t ret = RT_MTD_EOK;
    rt_nand_driver_t chip = MTD_NAND_DRV(device);

    if (data == RT_NULL || data_len == 0)
    {
        ret = _only_write_oob(chip, page, spare, spare_len);
    }
    else
    {
        ret = _do_write_desc(chip, page, data, data_len, spare, spare_len);
    }

    return ret;
}

static rt_err_t _move_page(rt_mtd_nand_t device, rt_off_t src_page, rt_off_t dst_page)
{
    return RT_MTD_EOK;
}

static rt_err_t _erase_block(rt_mtd_nand_t device, rt_int32_t block)
{
    rt_err_t ret = RT_EOK;
    rt_nand_driver_t chip = MTD_NAND_DRV(device);

    ret = chip->ops->cmdfunc(chip, RT_NAND_CMD_BLK_ERASE, block, 0);

    return ret;
}

static rt_err_t _check_block(rt_mtd_nand_t device, rt_int32_t block)
{
    rt_err_t ret;
    rt_nand_driver_t chip = MTD_NAND_DRV(device);

    if (chip->ops->checkbad)
    {
        ret = chip->ops->checkbad(chip, block);
    }
    else
    {
        int page = block * chip->parent.pages_per_block;
        _read_oob_std(chip, page);
        ret = chip->buffers.oob_poi[0] != 0xFF;
    }

    return ret;
}

static rt_err_t _mark_badblock(rt_mtd_nand_t device, rt_int32_t block)
{
    rt_err_t ret = RT_MTD_EOK;
    rt_nand_driver_t chip = MTD_NAND_DRV(device);

    if (chip->ops->markbad)
    {
        ret = chip->ops->markbad(chip, block);
    }
    else
    {
        int page = block * chip->parent.pages_per_block;
        rt_memset(chip->buffers.oob_poi, 0xFF, chip->parent.oob_size);
        chip->buffers.oob_poi[0] = 0;
        ret = _write_oob_std(chip, page);
    }

    return ret;
}

static rt_uint32_t _read_id(rt_mtd_nand_t device)
{
    rt_uint32_t id;
    rt_nand_driver_t chip = MTD_NAND_DRV(device);
    id = chip->ops->read_id();
    return id;
}

/**
 * RT-Thread Generic Device Interface
 */
static rt_err_t _mtd_init(rt_device_t dev)
{
    return RT_MTD_EOK;
}

static rt_err_t _mtd_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_MTD_EOK;
}

static rt_err_t _mtd_close(rt_device_t dev)
{
    return RT_MTD_EOK;
}

static rt_size_t _mtd_read(rt_device_t dev,
                           rt_off_t pos,
                           void *buffer,
                           rt_size_t size)
{
    /* return read size (count of block) */
    return size;
}

static rt_size_t _mtd_write(rt_device_t dev,
                            rt_off_t pos,
                            const void *buffer,
                            rt_size_t size)
{
    return size;
}

static rt_err_t _mtd_control(rt_device_t dev, int cmd, void *args)
{
    rt_ubase_t block;
    rt_err_t result = RT_MTD_EOK;
    rt_mtd_nand_t nand = RT_NULL;

    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(args != RT_NULL);

    nand = (rt_mtd_nand_t) dev;

    switch (cmd)
    {
    case RT_MTD_CTRL_GET_ID :
    {
        rt_uint32_t id;
        id = nand->ops->read_id(nand);
        *(rt_uint32_t*)args = id;
    }
    break;
    case RT_MTD_CTRL_ERASE :
        block = *(rt_uint32_t*)args;
        if (block > nand->block_end - nand->block_start)
        {
            return -RT_MTD_ESRC;
        }
        nand->ops->erase_block(nand, block + nand->block_start);
    break;
    case RT_MTD_CTRL_CHECK :
        if (nand->ops->check_block == RT_NULL)
        {
            return RT_MTD_EOK;
        }
        block = *(rt_uint32_t*)args;
        if (block > nand->block_end)
        {
            return -RT_MTD_ESRC;
        }
        nand->ops->check_block(nand, block + nand->block_start);
    break;
    case RT_MTD_CTRL_MARK :
        if (nand->ops->mark_badblock == RT_NULL)
        {
            return RT_MTD_EOK;
        }
        block = *(rt_uint32_t*)args;
        if (block > nand->block_end)
        {
            return -RT_MTD_ESRC;
        }
        nand->ops->mark_badblock(nand, block + nand->block_start);
    break;
    case RT_MTD_CTRL_READ :
    {
        struct rt_mtd_nand_rw_args *rw;
        rw = (struct rt_mtd_nand_rw_args*)args;
        if (rw->page < 0 || rw->page > nand->block_total * nand->pages_per_block)
        {
            return -RT_MTD_ESRC;
        }
        if ((rw->data && rw->data_len > nand->page_size) ||
            (rw->spare && rw->spare_len > nand->oob_size))
        {
            return -RT_MTD_ESRC;
        }
        result = nand->ops->read_page(nand,
                                        rw->page + nand->block_start * nand->pages_per_block,
                                        rw->data, rw->data_len, rw->spare, rw->spare_len);
    }
    break;
    case RT_MTD_CTRL_WRITE :
    {
        struct rt_mtd_nand_rw_args *rw;
        rw = (struct rt_mtd_nand_rw_args*)args;
        if (rw->page < 0 || rw->page > nand->block_total * nand->pages_per_block)
        {
            return -RT_MTD_ESRC;
        }
        if ((rw->data && rw->data_len > nand->page_size) ||
            (rw->spare && rw->spare_len > nand->oob_size))
        {
            return -RT_MTD_ESRC;
        }
        nand->ops->write_page(nand,
                                rw->page + nand->block_start * nand->pages_per_block,
                                rw->data, rw->data_len, rw->spare, rw->spare_len);
    }
    break;
    default:
    break;
    }
    return result;
}

static const struct rt_mtd_nand_driver_ops _nand_drv_ops =
{
    _read_id,
    _read_page,
    _write_page,
    _move_page,
    _erase_block,
    _check_block,
    _mark_badblock,
};

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops mtd_nand_ops =
{
    _mtd_init,
    _mtd_open,
    _mtd_close,
    _mtd_read,
    _mtd_write,
    _mtd_control
};
#endif

rt_err_t rt_mtd_nand_register_device(const char                *name,
                                     struct rt_mtd_nand_device *device)
{
    rt_device_t dev;
    rt_uint8_t *buf = RT_NULL;
    rt_nand_driver_t chip = MTD_NAND_DRV(device);

    RT_ASSERT(device != RT_NULL);

    device->ops = &_nand_drv_ops;

    buf = rt_malloc(device->oob_size * 3);
    if (buf == RT_NULL)
    {
        return -RT_ENOMEM;
    }

    chip->buffers.oob_poi = buf;
    buf += chip->parent.oob_size;
    chip->buffers.ecc_calc = buf;
    buf += chip->parent.oob_size;
    chip->buffers.ecc_code = buf;
    chip->buffers.pagebuf = 0;          /* alloc when unaligen access */

    dev = RT_DEVICE(device);

    /* set device class and generic device interface */
    dev->type        = RT_Device_Class_MTD;
#ifdef RT_USING_DEVICE_OPS
    dev->ops         = &mtd_nand_ops;
#else
    dev->init        = _mtd_init;
    dev->open        = _mtd_open;
    dev->read        = _mtd_read;
    dev->write       = _mtd_write;
    dev->close       = _mtd_close;
    dev->control     = _mtd_control;
#endif

    dev->rx_indicate = RT_NULL;
    dev->tx_complete = RT_NULL;

    /* register to RT-Thread device system */
    return rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
}

#if defined(RT_MTD_NAND_DEBUG) && defined(RT_USING_FINSH)
#include <finsh.h>
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')

static void mtd_dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
{
    unsigned char *buf = (unsigned char *)ptr;
    int i, j;
    for (i = 0; i < buflen; i += 16)
    {
        rt_kprintf("%06x: ", i);
        for (j = 0; j < 16; j++)
            if (i + j < buflen)
            {
                rt_kprintf("%02x ", buf[i + j]);
            }
            else
            {
                rt_kprintf("   ");
            }
        rt_kprintf(" ");
        for (j = 0; j < 16; j++)
            if (i + j < buflen)
            {
                rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
            }
        rt_kprintf("\n");
    }
}

int mtd_nandid(const char *name)
{
    struct rt_mtd_nand_device *nand;
    nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
    if (nand == RT_NULL)
    {
        rt_kprintf("no nand device found!\n");
        return -RT_ERROR;
    }

    return rt_mtd_nand_read_id();
}

int mtd_nand_read(const char *name, int block, int page)
{
    rt_err_t result;
    rt_uint8_t *page_ptr;
    rt_uint8_t *oob_ptr;
    struct rt_mtd_nand_device *nand;

    nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
    if (nand == RT_NULL)
    {
        rt_kprintf("no nand device found!\n");
        return -RT_ERROR;
    }

    page_ptr = rt_malloc(nand->page_size + nand->oob_size);
    if (page_ptr == RT_NULL)
    {
        rt_kprintf("out of memory!\n");
        return -RT_ENOMEM;
    }

    oob_ptr = page_ptr + nand->page_size;
    rt_memset(page_ptr, 0xff, nand->page_size + nand->oob_size);

    /* calculate the page number */
    page = block * nand->pages_per_block + page;
    result = rt_mtd_nand_read(nand, page, page_ptr, nand->page_size,
                              oob_ptr, nand->oob_size);

    rt_kprintf("read page, rc=%d\n", result);
    mtd_dump_hex(page_ptr, nand->page_size);
    mtd_dump_hex(oob_ptr, nand->oob_size);

    rt_free(page_ptr);
    return 0;
}

int mtd_nand_readoob(const char *name, int block, int page)
{
    struct rt_mtd_nand_device *nand;
    rt_uint8_t *oob_ptr;

    nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
    if (nand == RT_NULL)
    {
        rt_kprintf("no nand device found!\n");
        return -RT_ERROR;
    }

    oob_ptr = rt_malloc(nand->oob_size);
    if (oob_ptr == RT_NULL)
    {
        rt_kprintf("out of memory!\n");
        return -RT_ENOMEM;
    }

    /* calculate the page number */
    page = block * nand->pages_per_block + page;
    rt_mtd_nand_read(nand, page, RT_NULL, nand->page_size,
                     oob_ptr, nand->oob_size);
    mtd_dump_hex(oob_ptr, nand->oob_size);

    rt_free(oob_ptr);
    return 0;
}

int mtd_nand_write(const char *name, int block, int page)
{
    rt_err_t result;
    rt_uint8_t *page_ptr;
    rt_uint8_t *oob_ptr;
    rt_uint32_t index;
    struct rt_mtd_nand_device *nand;

    nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
    if (nand == RT_NULL)
    {
        rt_kprintf("no nand device found!\n");
        return -RT_ERROR;
    }

    page_ptr = rt_malloc(nand->page_size + nand->oob_size);
    if (page_ptr == RT_NULL)
    {
        rt_kprintf("out of memory!\n");
        return -RT_ENOMEM;
    }

    oob_ptr = page_ptr + nand->page_size;
    /* prepare page data */
    for (index = 0; index < nand->page_size; index++)
    {
        page_ptr[index] = index & 0xff;
    }
    /* prepare oob data */
    for (index = 0; index < nand->oob_size; index++)
    {
        oob_ptr[index] = index & 0xff;
    }

    /* calculate the page number */
    page = block * nand->pages_per_block + page;
    result = rt_mtd_nand_write(nand, page, page_ptr, nand->page_size,
                               oob_ptr, nand->oob_size);
    if (result != RT_MTD_EOK)
    {
        rt_kprintf("write page failed!, rc=%d\n", result);
    }

    rt_free(page_ptr);
    return 0;
}

int mtd_nand_erase(const char *name, int block)
{
    struct rt_mtd_nand_device *nand;
    nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
    if (nand == RT_NULL)
    {
        rt_kprintf("no nand device found!\n");
        return -RT_ERROR;
    }

    return rt_mtd_nand_erase_block(nand, block);
}

int mtd_nand_erase_all(const char *name)
{
    rt_uint32_t index = 0;
    struct rt_mtd_nand_device *nand;

    nand = RT_MTD_NAND_DEVICE(rt_device_find(name));
    if (nand == RT_NULL)
    {
        rt_kprintf("no nand device found!\n");
        return -RT_ERROR;
    }

    for (index = nand->block_start; index <= nand->block_end; index++)
    {
        rt_mtd_nand_erase_block(nand, index);
    }

    return 0;
}

#ifdef FINSH_USING_MSH
static void mtd_nand(int argc, char **argv)
{
    /* If the number of arguments less than 2 */
    if (argc < 3)
    {
help:
        rt_kprintf("\n");
        rt_kprintf("mtd_nand [OPTION] [PARAM ...]\n");
        rt_kprintf("         id       <name>            Get nandid by given name\n");
        rt_kprintf("         read     <name> <bn> <pn>  Read data on page <pn> of block <bn> of device <name>\n");
        rt_kprintf("         readoob  <name> <bn> <pn>  Read oob  on page <pn> of block <bn> of device <name>\n");
        rt_kprintf("         write    <name> <bn> <pn>  Run write test on page <pn> of block <bn> of device <name>\n");
        rt_kprintf("         erase    <name> <bn>       Erase on block <bn> of device <name>\n");
        rt_kprintf("         eraseall <name>            Erase all block on device <name>\n");
        return ;
    }
    else if (!strcmp(argv[1], "id"))
    {
        mtd_nandid(argv[2]);
    }
    else if (!strcmp(argv[1], "read"))
    {
        if (argc < 5)
        {
            rt_kprintf("The input parameters are too few!\n");
            goto help;
        }
        mtd_nand_read(argv[2], atoi(argv[3]), atoi(argv[4]));
    }
    else if (!strcmp(argv[1], "readoob"))
    {
        if (argc < 5)
        {
            rt_kprintf("The input parameters are too few!\n");
            goto help;
        }
        mtd_nand_readoob(argv[2], atoi(argv[3]), atoi(argv[4]));
    }
    else if (!strcmp(argv[1], "write"))
    {
        if (argc < 5)
        {
            rt_kprintf("The input parameters are too few!\n");
            goto help;
        }
        mtd_nand_write(argv[2], atoi(argv[3]), atoi(argv[4]));
    }
    else if (!strcmp(argv[1], "erase"))
    {
        if (argc < 4)
        {
            rt_kprintf("The input parameters are too few!\n");
            goto help;
        }
        mtd_nand_erase(argv[2], atoi(argv[3]));
    }
    else if (!strcmp(argv[1], "eraseall"))
    {
        mtd_nand_erase_all(argv[2]);
    }
    else
    {
        rt_kprintf("Input parameters are not supported!\n");
        goto help;
    }
}
MSH_CMD_EXPORT(mtd_nand, MTD nand device test function);
#endif /* FINSH_USING_MSH */

#ifndef FINSH_USING_MSH_ONLY
FINSH_FUNCTION_EXPORT_ALIAS(mtd_nandid, nand_id, read ID - nandid(name));
FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_read, nand_read, read page in nand - nand_read(name, block, page));
FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_readoob, nand_readoob, read spare data in nand - nand_readoob(name, block, page));
FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_write, nand_write, write dump data to nand - nand_write(name, block, page));
FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_erase, nand_erase, nand_erase(name, block));
FINSH_FUNCTION_EXPORT_ALIAS(mtd_nand_erase_all, nand_erase_all, erase all of nand device - nand_erase_all(name, block));
#endif /* FINSH_USING_MSH_ONLY */

#endif /* defined(RT_MTD_NAND_DEBUG) && defined(RT_USING_FINSH) */

#endif /* RT_USING_MTD_NAND */
